with Ada.Exceptions;
with Ada.Command_Line;
with Ada.Directories;
with Legend.Settings;
with Legend.Resources;
with Legend.Interactives;
with Legend.Game_Titles;
with Legend.Stages, Legend.Stages.Worlds, Legend.Stages.Rooms;
with Legend.Story_Engine;
with Legend.Music;
with Legend.Renders;
with Legend.Timers;
with XMI2MID;
with StrEnc;
with Performances;
package body Legend.Application is

   Global_Resources_Is_Ready : Boolean := False;

   procedure Load_Config
   is
   begin
      config.Attach("./legend.conf");
      config.Load;
      config.Read("player.walk.speed", Settings.Player.Walk.Speed);
      config.Read("npc.walk.speed", Settings.NPC.Walk.Speed);
      config.Read("menu.font.size", Settings.Menu.Font.Size);
      config.Read("menu.font.name", Settings.Menu.Font.Name);
      Config.Read("video.fullscreen", Settings.Video.Fullscreen);
      Config.Read("video.screen.width", Settings.Video.Screen_Width);
      Config.Read("video.screen.height", Settings.Video.Screen_Height);
   end Load_Config;

   procedure Load_Global_Resources
   is
      p : Performances.Monitor_Type;
   begin
      if not Global_Resources_Is_Ready then
         p.Start;
         Resources.Load_Talk_Resources;
         Resources.Load_War_Define_Resources;
         Resources.Load_Picture_Resources;
         p.Stop;
         Trace_Log("loaded resources in " & p.Average'Img & " seconds");
         Global_Resources_Is_Ready := True;
      end if;
   end Load_Global_Resources;

   procedure Show_Menu (
      Level : out Game_Titles.Menu_Level_Type; 
      Focus : out Uint16_t
      )
   is
      title : Game_Titles.Game_Title_Type;
   begin
      title.Canvas.Invalidate;
      title.Run;
      Level := title.Level;
      Focus := title.Focus;
   end Show_Menu;

   procedure Show_Dead
   is
      dead : Game_Titles.Game_Over_Type;
   begin
      dead.Canvas.Invalidate;
      dead.Run;
   end Show_Dead;

   procedure Start_Game 
   is
      World : aliased Legend.Stages.Worlds.World_Type;
   begin
      Music.Play_Background_Music(16);
      Load_Global_Resources;
      World.Canvas.Bind;
      World.Setup(
         Map_Size  => (x => Legend.World_Width, y => Legend.World_Height),
         Grid_Size => (x => 18, y => 9),
         Pictures  => Resources.Outer_Images);
      World.Run;
      if Gameover then
         Show_Dead;
      end if;
   end Start_Game;

   -- 序幕
   procedure Prologue 
   is
      Story : Story_Type renames All_Scene_Stories(Resources.Leader_Home)(0);
      Home : aliased Stages.Rooms.Room_Type;
      -- story 0691
   begin
      Load_Global_Resources;
      Resources.Load_Room_Pictures(Resources.Leader_Home);
      Home.Canvas.Bind;
      Home.Setup(
         Map_Size  => (x => Room_Width, y => Room_Height),
         Grid_Size => (x => 18, y => 9),
         Pictures  => Resources.Inner_Images(Resources.Leader_Home),
         Scene     => All_World.Scenes(Resources.Leader_Home)'Access);
      Home.MySelf.Position(Story.Position); -- - Face_To_Offset(Home.Myself.Face));
      Home.Myself.Focus;
      Home.Canvas.Repaint;
--      Home.Myself.Start_Walk;
      Home.Run;
   end Prologue;

   procedure Create_Role_Name
   is
      nc : Game_Titles.Name_Creator_Type;
   begin
      nc.Run;
      if not nc.Name.Is_Empty then
         declare
            use Interfaces.C;
            Leader : Hero_Type renames All_World.Heroes(0);
            name   : String := Game_Titles.To_String(nc.Name);
            s      : Big5_Name := To_C(StrEnc.UTF8_To_BIG5.Convert(name));
         begin
            Leader.Name := (1 .. Leader.Name'Length => nul);
            if s'Length < Leader.Name'Length then
               Leader.Name(1 .. s'Length) := s;
            else
               Leader.Name := s(s'First .. s'First + Leader.Name'Length - 1);
            end if;
         end;
      end if;
   end Create_Role_Name;

   procedure Create_Role_Attr
   is
      rc : Game_Titles.Role_Creator_Type;
   begin
      rc.Create_Random_Attr;
      rc.Run;
   end Create_Role_Attr;

   procedure Create_Role
   is
   begin
      Create_Role_Name;
      Create_Role_Attr;
   end Create_Role;

   procedure New_Game 
   is
   begin
      Set_Game(0);
      Load_States;
      Create_Role;
      Prologue;
      Start_Game;
   end New_Game;

   procedure Load_Game (Index : Uint16_t)
   is
   begin
      Set_Game(Index);
      Load_States;
      Start_Game;
   end Load_Game;

   procedure Select_Game
   is
      Level : Game_Titles.Menu_Level_Type;
      Focus : Uint16_t;
   begin
      Show_Menu(Level, Focus);
      case Level is
         when Game_Titles.ml_Main =>
            case Focus is
               when 0      => New_Game;
               when others => Set_Game(No_Game);
            end case;
         when Game_Titles.ml_Load => 
            Load_Game(Focus + 1);
      end case;
   end Select_Game;

   procedure Initialize 
   is
      use Ada.Command_Line, Ada.Directories;
   begin
      Trace_Log("sizeof(hero) = " & Hero_Type'Max_Size_In_Storage_Elements'Img);
      Trace_Log("sizeof(War_Define_Type) = " & War_Define_Type'Max_Size_In_Storage_Elements'Img);
      Set_Directory(Containing_Directory(Command_Name));
      GNAT.Random_Numbers.Reset(Rnd_Generator);
      Load_Config;
      if not Exists(JY_Path & "ALLSIN.GRP") then
         raise Program_Error with "Game resources not found! " & 
         "please modify legend.path in legend.conf first.";
      end if;
      XMI2MID(JY_Path);
      Trace_Log("Init SDL"); 
      if not SDL.Init(Flags => SDL.Init_Video or SDL.Init_Audio) then
         raise SDL_Error with "SDL.Init " & SDL.Error.Get_Error;
      end if;
      Renders.Initialize;
      Music.Initialize;
      Resources.Load_Palette_Resources;
      Resources.Load_Title_Resources;
      Timers.World_Clock.Start;
   end Initialize;

   procedure Finalize
   is
      screen : SDL.Video.Surface_Access_t := Renders.Screen;
   begin
      Music.Finalize;
      Timers.World_Clock.Stop;
      Renders.Finalize;
      SDL.Quit;
      Trace_Log("Done");
   end Finalize;

   procedure Main_Loop
   is
   begin
      Select_Game;
      while Data_Files.Current_Index /= No_Game loop
         Load_States;
         Start_Game;
      end loop;
   exception
      when Error : others =>
         Trace_Log(Ada.Exceptions.Exception_Information(Error));
   end Main_Loop;

   procedure Run 
   is
   begin
      Initialize;
      Main_Loop;
      Finalize;
   end Run;

end Legend.Application;
